package org.ws.httphelper.support.pipeline;

import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.ws.httphelper.core.pipeline.HttpHandler;
import org.ws.httphelper.core.pipeline.HttpHandlerContext;
import org.ws.httphelper.core.pipeline.HttpPipeline;
import org.ws.httphelper.core.pipeline.RequestHandler;
import org.ws.httphelper.core.pipeline.ResponseHandler;
import org.ws.httphelper.exception.ResponseException;
import org.ws.httphelper.model.RequestContext;
import org.ws.httphelper.model.http.ResponseFuture;

import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.Map;
import java.util.concurrent.ExecutorService;

/**
 * HandlerContext抽象类.
 * Context链表的节点实现.
 *
 */
public abstract class AbstractHandlerContext implements HttpHandlerContext {

    private static Logger log = LoggerFactory.getLogger(AbstractHandlerContext.class.getName());

    /**
     * 下一个context
     */
    protected volatile AbstractHandlerContext next;
    /**
     * 上一个context
     */
    protected volatile AbstractHandlerContext prev;
    /**
     * 当前pipeline
     */
    private final DefaultHttpPipeline pipeline;
    /**
     * 当前context对应的异步执行器
     */
    private final ExecutorService executor;
    /**
     * 已匹配缓存的RequestHandler
     */
    private static final Map<Class<? extends RequestHandler>, Class> CACHE_REQUEST_HANDLERS = Maps.newHashMap();
    /**
     * 已匹配缓存的ResponseHandler
     */
    private static final Map<Class<? extends ResponseHandler>, Class> CACHE_RESPONSE_HANDLERS = Maps.newHashMap();

    public AbstractHandlerContext(DefaultHttpPipeline pipeline, ExecutorService executor) {
        this.pipeline = pipeline;
        this.executor = executor;
    }

    /**
     * 获取排序值
     * @return
     */
    public abstract int getOrder();

    /**
     * 获取当前pipeline
     * @return
     */
    @Override
    public HttpPipeline pipeline() {
        return pipeline;
    }

    /**
     * 获取当前异步执行executor
     * @return
     */
    @Override
    public ExecutorService executor() {
        return executor;
    }

    /**
     * 执行request处理
     * @param requestContext
     */
    @Override
    public void request(RequestContext requestContext) {
        invokeRequest(requestContext,findContextRequest());
    }

    /**
     * 实际执行request处理逻辑
     * @param requestContext
     * @param next
     */
    static void invokeRequest(final RequestContext requestContext, final AbstractHandlerContext next){
        // 没有下一个,直接返回
        if(next == null){
            return;
        }
        // 没有处理器,直接返回
        HttpHandler handler = next.handler();
        if(handler == null){
            return;
        }
        if(log.isDebugEnabled()) {
            log.debug("execute request handler -> {}", handler.getClass().getName());
        }
        // 执行处理,同步串行执行
        // 根据返回值判断是否执行后续handler
        boolean result = ((RequestHandler) handler).handler(requestContext);
        // 同步情况等待处理结果
        if(result){
            // 执行下一个
            next.request(requestContext);
        }
    }

    /**
     * 获取下一个requestContext
     * @return
     */
    private AbstractHandlerContext findContextRequest(){
        // 从当前context向后遍历
        AbstractHandlerContext context = this;
        do {
            // 向后遍历：查找next requestHandler
            context = context.next;
        }
        while (context != null && !matchRequest(context.handler()));
        return context;
    }

    /**
     * 判断是否是requestHandler
     * @param httpHandler
     * @return
     */
    private boolean matchRequest(HttpHandler httpHandler){
        if(!(httpHandler instanceof RequestHandler)){
            return false;
        }
        try {
            Class handlerClass = httpHandler.getClass();
            // 从缓存获取
            if(CACHE_REQUEST_HANDLERS.containsKey(handlerClass)) {
                Class cacheClass = CACHE_REQUEST_HANDLERS.get(handlerClass);
                return cacheClass != null;
            }
            else {
                // 根据方法判断
                boolean skippable = isMatchHandler(handlerClass, "handler",
                        RequestContext.class);
                if(skippable){
                    CACHE_REQUEST_HANDLERS.put(handlerClass,handlerClass);
                }
                return skippable;
            }
        } catch (Exception e) {
            log.error(e.getMessage(),e);
            return false;
        }
    }

    /**
     * 执行response处理
     * @param requestContext
     */
    @Override
    public void response(RequestContext requestContext) {
        invokeResponse(findContextResponse(),requestContext);
    }

    /**
     * 实际执行response处理逻辑
     * @param prev
     * @param requestContext
     */
    static void invokeResponse(final AbstractHandlerContext prev, final RequestContext requestContext){
        // 上一个为空，至今返回
        if(prev == null){
            return;
        }
        // handler为空，直接返回
        HttpHandler handler = prev.handler();
        if(handler == null){
            return;
        }
        if(log.isDebugEnabled()) {
            log.debug("execute response handler -> {}", handler.getClass().getName());
        }

        ExecutorService executor = prev.executor();
        // 存在异步执行器
        if(executor != null){
            // 异步处理
            executor.execute(()->{
                try {
                    ((ResponseHandler) handler).handler(requestContext);
                } catch (ResponseException e) {
                    log.error(e.getMessage(),e);
                }
            });
            // 异步情况不用等待处理结果：无法打断
            // 继续执行前一个的response
            prev.response(requestContext);
        }
        else {
            try {
                // 同步执行
                boolean result = ((ResponseHandler) handler).handler(requestContext);
                // 同步情况等待处理结果
                if(result){
                    // 继续执行前一个的response
                    prev.response(requestContext);
                }
            } catch (ResponseException e) {
                log.error(e.getMessage(),e);
            }
        }
    }

    /**
     * 查找前一个response
     * @return
     */
    private AbstractHandlerContext findContextResponse(){
        // 从当前开始查找
        AbstractHandlerContext context = this;
        do {
            // 前一个
            context = context.prev;
        }
        while (context != null && !matchResponse(context.handler()));
        return context;
    }

    /**
     * 匹配是否responseHandler
     * @param httpHandler
     * @return
     */
    private boolean matchResponse(HttpHandler httpHandler){
        if(!(httpHandler instanceof ResponseHandler)){
            return false;
        }
        try {
            Class handlerClass = httpHandler.getClass();
            // 从缓存查找
            if(CACHE_RESPONSE_HANDLERS.containsKey(handlerClass)) {
                Class cacheClass = CACHE_RESPONSE_HANDLERS.get(handlerClass);
                return cacheClass != null;
            }
            else {
                // 通过方法判断
                boolean isMatch = isMatchHandler(handlerClass, "handler",
                        RequestContext.class);
                if(isMatch){
                    CACHE_RESPONSE_HANDLERS.put(handlerClass, handlerClass);
                }
                return isMatch;
            }
        } catch (Exception e) {
            log.error(e.getMessage(),e);
            return false;
        }
    }

    /**
     * 通过方法判断是否符合
     * @param handlerType
     * @param methodName
     * @param paramTypes
     * @return
     * @throws Exception
     */
    private static boolean isMatchHandler(
            final Class<?> handlerType, final String methodName, final Class<?>... paramTypes) throws Exception {
        return AccessController.doPrivileged(new PrivilegedExceptionAction<Boolean>() {
            @Override
            public Boolean run() throws Exception {
                Method m;
                try {
                    m = handlerType.getMethod(methodName, paramTypes);
                } catch (NoSuchMethodException e) {
                    log.warn("Class {} missing method {}, assume we can not skip execution", handlerType, methodName, e);
                    return false;
                }
                return m != null;
            }
        });
    }
}
